classdef dotsQueryableHID < dotsQueryable
    % @class dotsQueryableHID
    % Superclass for objects that acquire and classify data from Human
    % Interface Devices.
    % @details
    % dotsQueryableHID extends the dotsQueryable superclass with support
    % for Human Interface Devices (HID).  It uses the mexHID() mex
    % function, which is part of Snow Dots, to locate and communicate with
    % HID devices.
    % @details
    % dotsQueryableHID itself is not a usable class.  It supports 
    % dotsQueryableHID* subclasses such as dotsQueryableHIDKeyboard.
    % @details
    % mexHID() uses an internal queue to record all the value changes of
    % elements (buttons, axes, etc.) for each dotsQueryableHID object.  The
    % dotsQueryableHID readData() method passes any queued data into Matlab
    % via each object's appendData() method.
    % @ingroup queryable
    
    properties
        % struct of HID parameters used to identify a suitable HID device.
        % Each subclass should supply parameters pand values that mexHID()
        % can use to locate a suitable HID device.  Extending the
        % openMexHIDDevice() would be one good way to do this.
        % @details
        % See mexHIDUsage() or mexHIDUsage.gui() for lists of valid HID
        % parameters.  Use dotsTheMachineConfiguration to specify
        % machine-specific paramers or values.
        deviceMatching;
        
        % device identifier as returned from mexHID('openMatchingDevice').
        % Filled in automatically by openMexHIDDevice().
        deviceID;
        
        % array of element cookies as returned from
        % mexHID('findMatchingElements'). Each subclass should fill in
        % queueCookies with the device element cookies that should be added
        % to the device queue.  Other device elements will be ignored.
        % Extending the openMexHIDDevice() would be one good way to do
        % this.
        queueCookies;
        
        % size of the mexHID() internal data queue.
        % queueDepth is number of device element value changes to keep in
        % the mexHID() internal data queue, before discarding old values.
        % @details
        % The suitable queueDepth depends on how fast a HID device
        % accumulates data and how often the data are read into Matlab,
        % with readData().
        queueDepth = 1024;
        
        % function_handle invoked internally by mexHID('check') to pass
        % formatted data into Matlab.  The default, mexHIDQueueCallback()
        % is suitable for most purposes and probably should not be changed.
        % @details
        % Whatever function is used must expect a dotsQueryableHID object
        % (self) as the first argument.  It must expect a matrix of data as
        % the second argument.  The data will be formatted as specified for
        % the dotsQueryale allData property.
        queueCallback = @dotsQueryableHID.mexHIDQueueCallback;
    end
    
    methods
        % Constructor takes no arguments.
        % @details
        % User should use dotsTheQueryablesManager to create new objects.
        function self = dotsQueryableHID
            self = self@dotsQueryable;
        end

        % Release mexHID() resources for this device.
        % @details
        % Matlab calls delete() automatically when it's done using the
        % object.
        function delete(self)
            mexHID('closeDevice', self.deviceID);
        end
        
        % Try to locate a device and open a queue to record data.
        % @details
        % Returns true if mexHID() finds suitable device and is able to
        % open a queue to record device data.  Otherwise returns false.
        % @details
        % See openMexHIDDevice() and openMexHIDDevice().
        function isAvailable = checkIsAvailable(self)
            isAvailable = self.openMexHIDDevice;
            if isAvailable
                isAvailable = self.openMexHIDQueue;
            end
        end
        
        % Locate and a device and open it.
        % @details
        % Uses HID device parameters and values in deviceMatching to locate
        % a suitable device and open it for communication with mexHID().
        % @details
        % Returns true if mexHID() locates a device that matches the
        % parameters and values in deviceMatching, and it's able to open
        % the device for communication.  Otherwise returns false.
        function isOpen = openMexHIDDevice(self)
            isOpen = false;
            if exist('mexHID', 'file')
                
                if ~mexHID('isInitialized')
                    mexHID('initialize');
                end
                
                self.closeMexHIDDevice;
                self.deviceID = mexHID('openMatchingDevice', self.deviceMatching);
                isOpen = self.deviceID > 0;
            end
        end
        
        % Open a queue to record value changes for device elements.
        % @details
        % Adds the device elements identified by queueCookies to a queue to
        % have their value changes recorded.  Other device elements will be
        % left out of the queue, so their values will be ignored.
        % @details
        % Returns true if mexHID successfully created the queue and added
        % queueCookies elements were added to it.  The queue will start
        % recording value changes immediately.
        function isOpen = openMexHIDQueue(self)
            isOpen = false;
            if ~isempty(self.queueCookies) && all(self.queueCookies > 0)
                
                qcb = {self.queueCallback, self};
                queueStatus = mexHID('openQueue', ...
                    self.deviceID, self.queueCookies, qcb, self.queueDepth);
                
                if queueStatus >= 0
                    queueStatus = mexHID('startQueue', self.deviceID);
                end
                isOpen = queueStatus >= 0;
            end
        end
        
        % Release mexHID() resources for this device.
        function closeMexHIDDevice(self)
            mexHID('closeDevice', self.deviceID);
            self.deviceID = -1;
        end
        
        % Pass any data from the mexHID() internal queue to appendData().
        % @details
        % Moves recorded data from the mexHID() internal queue into Matlab
        % by passing formatted data to this object's appendData() method.
        % @details
        % Calling readData() for this object will affect all other
        % dotsQueryableHID objects as though their readData() method had
        % been called at the same time.
        function readData(self)
            mexHID('check');
        end
        
        % Clear data from this object and the mexHID() internal queue.
        % @details
        % Extends the dotsQueryable flushData() method to do also flush the
        % mexHID() internal data queue.
        function flushData(self)
            self.flushData@dotsQueryable;
            if self.isAvailable
                mexHID('flushQueue', self.deviceID);
                mexHID('startQueue', self.deviceID);
            end
        end
    end
    
    methods (Static)
        % Pass data from the mexHID() internal queue to Matlab.
        % @param self the dotsQueryableHID object that should receive
        % @a newData.
        % @param newData double matrix of device element value changes from
        % the HID device that @a self is managing.  @a newData  will be
        % formatted as specified for the dotsQueryale allData property.
        % @details
        % This is the default function called automatically during the
        % dotsQueryableHID readData() method, or by mexHID('check').  It
        % passes recieves formatted data from the mexHID() mex function.
        function mexHIDQueueCallback(self, newData)
            self.appendData(newData);
        end
    end
end